﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;

namespace mmCore.Common
{
    public class WebFetch
    {
        #region MEMBERS

        private readonly string _mStrUri;
        private static int _mNCurrentWorkers;
        private const int MnMaxWorkers = 5;
        private const int MnTimeout = 100;
        private bool _mBActive;
        private readonly List<Tuple<string, string>> _postArgs;

        #endregion MEMBERS

        #region EVENTS

        /// <summary>
        /// Called after fetching a web page.
        /// </summary>
        public event EventHandler<FetchEventArgs> OnFinished;

        #endregion EVENTS

        #region METHODS

        /// <summary>
        /// Constructor.
        /// </summary>
        public WebFetch(string aStrUri, List<Tuple<string,string>> postArgs)
        {
            _mStrUri = aStrUri;
            _postArgs = postArgs;
        }

        /// <summary>
        /// Constructor.
        /// </summary>
        public WebFetch(string aStrUri)
        {
            _mStrUri = aStrUri;
            _postArgs = null;
        }

        /// <summary>
        /// Constructor.
        /// </summary>
        public void Fetch()
        {
            Fetch(null);
        }

        /// <summary>
        /// Fetch a web page and pass the argument to the callback.
        /// </summary>
        /// <param name="aOArg"></param>
        public void Fetch(object aOArg)
        {
            _mBActive = true;
            if (_mNCurrentWorkers >= MnMaxWorkers)
            {
                // we don't want to block calling thread, so we wait in another one.
                ThreadPool.QueueUserWorkItem((o) =>
                {
                    while (_mNCurrentWorkers >= MnMaxWorkers)
                    {
                        Thread.Sleep(MnTimeout);
                        if (!_mBActive)
                        {
                            return;
                        }
                    }
                    ThreadPool.QueueUserWorkItem(Process, aOArg);
                }, aOArg);
}
            else
            {
                ThreadPool.QueueUserWorkItem(Process, aOArg);
            }
        }

        /// <summary>
        /// Cancel fetching thread.
        /// </summary>
        public void Cancel()
        {
            _mBActive = false;
        }

        #endregion METHODS

        #region PRIVATE METHODS

        private void Process(object aOArg)
        {
            Interlocked.Increment(ref _mNCurrentWorkers);

            try
            {
                var sb = new StringBuilder();
                var buf = new byte[8192];

                var request = (HttpWebRequest)WebRequest.Create(_mStrUri);

                if (_postArgs != null && _postArgs.Count > 0)
                {
                    request.Method = "POST";
                    request.ContentType = "application/x-www-form-urlencoded";
                    var sbArgs = new StringBuilder();
                    foreach (var arg in _postArgs)
                    {
                        if (sbArgs.Length != 0)
                        {
                            sbArgs.Append("&");
                        }
                        sbArgs.Append(string.Format("{0}={1}", arg.Item1, arg.Item2));
                    }
                    request.ContentLength = sbArgs.Length;
                    // Write the request
                    var stOut = new StreamWriter(request.GetRequestStream(), Encoding.ASCII);
                    stOut.Write(sbArgs.ToString());
                    stOut.Close();
                }

                var response = (HttpWebResponse)request.GetResponse();

                var resStream = response.GetResponseStream();
                var count = 0;

                do
                {
                    if (resStream != null) count = resStream.Read(buf, 0, buf.Length);
                    if (count == 0) continue;
                    var tempString = Encoding.GetEncoding("ISO-8859-2").GetString(buf, 0, count);
                    sb.Append(tempString);
                } while (count > 0 && _mBActive);

                Interlocked.Decrement(ref _mNCurrentWorkers);

                if (OnFinished != null && _mBActive)
                {
                    OnFinished(this, new FetchEventArgs(sb.ToString(), aOArg));
                }
            }
            catch
            {
                Interlocked.Decrement(ref _mNCurrentWorkers);
                return;
            }
        }

        #endregion PRIVATE METHODS
    }

    #region EVENTSARG

    public class FetchEventArgs : EventArgs
    {
        public string Result { set; get; }
        public object Data { set; get; }

        public FetchEventArgs(string aStrResult)
        {
            Result = aStrResult;
            Data = null;
        }

        public FetchEventArgs(string aStrResult, object aOData)
        {
            Result = aStrResult;
            Data = aOData;
        }
    }

    #endregion EVENTSARG
}
